package com.atguigu.tingshu.user.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.WxMaUserService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import com.atguigu.tingshu.album.AlbumFeignClient;
import com.atguigu.tingshu.common.cache.GuiguCache;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.rabbit.constant.MqConst;
import com.atguigu.tingshu.common.rabbit.service.RabbitService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.user.*;
import com.atguigu.tingshu.user.mapper.*;
import com.atguigu.tingshu.user.service.UserInfoService;
import com.atguigu.tingshu.user.service.strategy.PaidRecordStrategy;
import com.atguigu.tingshu.user.service.strategy.factory.StrategyFactory;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.error.WxErrorException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@SuppressWarnings({"all"})
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Autowired
    private WxMaService wxMaService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private UserPaidAlbumMapper userPaidAlbumMapper;

    @Autowired
    private UserPaidTrackMapper userPaidTrackMapper;

    @Autowired
    private AlbumFeignClient albumFeignClient;

    @Autowired
    private UserVipServiceMapper userVipServiceMapper;

    @Autowired
    private VipServiceConfigMapper vipServiceConfigMapper;

    @Autowired
    private StrategyFactory strategyFactory;

    /**
     * 微信登录
     *
     * @param code
     * @return 登录之后的token
     */
    @Override
    public Map<String, String> wxLogin(String code) {
        try {
            //1.java服务端对接微信登录SDK，获取微信用户账户唯一标识
            WxMaUserService userService = wxMaService.getUserService();
            WxMaJscode2SessionResult sessionInfo = userService.getSessionInfo(code);
            String wxOpenId = sessionInfo.getOpenid();

            //2.根据微信唯一标识wxOpenId，查询用户记录
            LambdaQueryWrapper<UserInfo> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(UserInfo::getWxOpenId, wxOpenId);
            UserInfo userInfo = userInfoMapper.selectOne(queryWrapper);

            //3.判断如果微信唯一标识未绑定用户记录-说明首次登录。
            if (userInfo == null) {
                //3.1 创建用户记录绑定微信唯一标识
                userInfo = new UserInfo();
                userInfo.setAvatarUrl("https://ts2.tc.mm.bing.net/th/id/OIP-C.nHM69bbue7MCBazkzc1VCwHaNK?rs=1&pid=ImgDetMain&o=7&rm=3");
                userInfo.setWxOpenId(wxOpenId);
                userInfo.setNickname("听友" + IdUtil.getSnowflakeNextId());
                userInfoMapper.insert(userInfo);

                //3.2 发送初始化账户信息消息到RabbitMQ中
                //3.2.1 封装初始化账户所需参数Map
                Map<String, Object> msgData = new HashMap<>();
                msgData.put("userId", userInfo.getId());
                msgData.put("title", "首次登录赠送体验金");
                msgData.put("amount", new BigDecimal(10));
                msgData.put("orderNo", "zs" + IdUtil.getSnowflakeNextIdStr());

                //3.2.2 发送可靠性消息
                rabbitService.sendMessage(MqConst.EXCHANGE_USER, MqConst.ROUTING_USER_REGISTER, msgData);

            }

            //4.登录成功，生成token
            //4.1 登录成功，生成token
            String token = IdUtil.randomUUID();
            String loginKey = RedisConstant.USER_LOGIN_KEY_PREFIX + token;

            //4.2 将用户信息转为用户基本信息UserInfoVo
            UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);

            //4.3 将用户基本信息存入redis
            redisTemplate.opsForValue().set(
                    loginKey,
                    userInfoVo,
                    RedisConstant.USER_LOGIN_KEY_TIMEOUT,
                    TimeUnit.SECONDS);

            //4.4 封装令牌响应给客户端
            Map<String, String> map = new HashMap<>();
            map.put("token", token);
            return map;
        } catch (WxErrorException e) {
            log.error("[用户服务]微信登录异常:{}", e);
            throw new RuntimeException(e);
        }

    }

    /**
     * 获取用户信息
     *
     * @param userId
     * @return
     */
    @Override
    @GuiguCache(prefix = "user:info:")
    public UserInfoVo getUserInfo(Long userId) {
        UserInfo userInfo = userInfoMapper.selectById(userId);
        UserInfoVo userInfoVo = BeanUtil.copyProperties(userInfo, UserInfoVo.class);
        return userInfoVo;
    }

    /**
     * 更新当前用户的基本信息
     *
     * @param userInfoVo
     */
    @Override
    public void updateUser(UserInfoVo userInfoVo) {
        Long userId = AuthContextHolder.getUserId();
        UserInfo userInfo = new UserInfo();

        userInfo.setId(userId);
        userInfo.setNickname(userInfoVo.getNickname());
        userInfo.setAvatarUrl(userInfoVo.getAvatarUrl());

        userInfoMapper.updateById(userInfo);

    }

    /**
     * 判断提交的声音id购买状态
     *
     * @param userId
     * @param albumId
     * @param needCheckPayStatussTrackIdList
     * @return
     */
    @Override
    public Map<Long, Integer> userIsPaidTrack(Long userId, Long albumId, List<Long> needCheckPayStatussTrackIdList) {
        Map<Long, Integer> payStatusMap = new HashMap<>();

        //1.尝试根据用户id+专辑id查询已购买专辑表判断是否购买过专辑
        LambdaQueryWrapper<UserPaidAlbum> userPaidAlbumLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userPaidAlbumLambdaQueryWrapper.eq(UserPaidAlbum::getUserId, userId)
                .eq(UserPaidAlbum::getAlbumId, albumId)
                .last("limit 1");
        Long count = userPaidAlbumMapper.selectCount(userPaidAlbumLambdaQueryWrapper);

        //2.如果购买过专辑，则将提交的声音id列表全部标记为已购买=1 返回，结束
        //因为购买了专辑，所以所有的声音都已经购买了
        if (count > 0) {
            for (Long trackId : needCheckPayStatussTrackIdList) {
                payStatusMap.put(trackId, 1);
            }
            return payStatusMap;
        }

        //3.如果未购买专辑，则根据用户id+专辑id查询已购声音列表
        LambdaQueryWrapper<UserPaidTrack> userPaidTrackLambdaQueryWrapper = new LambdaQueryWrapper<>();
        userPaidTrackLambdaQueryWrapper.eq(UserPaidTrack::getUserId, userId)
                .eq(UserPaidTrack::getAlbumId, albumId)
                .select(UserPaidTrack::getTrackId);
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(userPaidTrackLambdaQueryWrapper);

        //4.如果已购声音列表为空，则将提交的声音id列表全部标记为未购买=0，返回，结束。
        if (CollectionUtil.isEmpty(userPaidTrackList)) {
            //说明专辑和声音都没有买，则将所有的专辑id设置为0，表示未购买。
            for (Long trackId : needCheckPayStatussTrackIdList) {
                payStatusMap.put(trackId, 0);
            }
            return payStatusMap;
        }


        //5.如果已购声音列表有值，遍历待检查声音id列表判断哪些已购哪些未购买，返回，结束。
        //已购声音列表
        List<Long> userPaidTrackIdList = userPaidTrackList.stream()
                .map(UserPaidTrack::getTrackId)
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(userPaidTrackList)) {
            for (Long needCheckPayStatussTrackId : needCheckPayStatussTrackIdList) {
                //如果已购声音id集合中包含待检查的声音id，则将购买状态标记为已购买=1
                //作用：检查列表userPaidTrackIdList中是否包含特定元素
                //needCheckPayStatussTrackId，如果包含返回true，否则返回false
                if (userPaidTrackIdList.contains(needCheckPayStatussTrackId)) {
                    //如果有，设置为已购买
                    payStatusMap.put(needCheckPayStatussTrackId, 1);
                } else {
                    //反之则标记为未购买=0
                    payStatusMap.put(needCheckPayStatussTrackId, 0);
                }

            }
        }
        return payStatusMap;
    }

    /**
     * 判断当前用户是否购买了指定专辑
     *
     * @param userId
     * @param albumId
     * @return
     */
    @Override
    public Boolean isPaidAlbum(Long userId, Long albumId) {
        //根据用户id+专辑id判断用户是否购买了专辑
        LambdaQueryWrapper<UserPaidAlbum> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidAlbum::getUserId, userId)
                .eq(UserPaidAlbum::getAlbumId, albumId);
        Long count = userPaidAlbumMapper.selectCount(queryWrapper);
        return count > 0;
    }

    /**
     * 查询当前用户指定专辑下已购声音列表
     *
     * @param userId
     * @param albumId
     * @return
     */
    @Override
    public List<Long> findUserPaidTrackIdList(Long userId, Long albumId) {
        //在user_paid_track表格中查找，根据userId和albumId查询
        LambdaQueryWrapper<UserPaidTrack> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserPaidTrack::getUserId, userId)
                .eq(UserPaidTrack::getAlbumId, albumId)
                .select(UserPaidTrack::getTrackId);

        //通过以上条件查询所有的trackIdList
        List<UserPaidTrack> userPaidTrackList = userPaidTrackMapper.selectList(queryWrapper);
        //我们只需要trackIdList即可
        Assert.notNull(userPaidTrackList, "用户已购专辑为空");

        if (CollectionUtil.isNotEmpty(userPaidTrackList)) {
            List<Long> userPaidTrackIdList = userPaidTrackList.stream()
                    .map(UserPaidTrack::getTrackId)
                    .collect(Collectors.toList());
            return userPaidTrackIdList;
        }
        return null;

    }

    /**
     * 用户付款成功后，给用户进行虚拟物品(VIP会员，专辑，声音)的发货
     *
     * @param userPaidRecordVo
     */
    @Override
    public void savePaidRecord(UserPaidRecordVo userPaidRecordVo) {

        String itemType = userPaidRecordVo.getItemType();
//        if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
//        } else if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
//        } else if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
//        }
        //1.根据货物类型从工厂中获取实现类对象
        PaidRecordStrategy strategy = strategyFactory.getStrategy(itemType);
        //2.调用对应实现类对象发货逻辑
        strategy.handlerPaidRecord(userPaidRecordVo);


    }
}
